
#include "slist_entry_heap.h"


namespace	memory
{
	slist_entry_heap::slist_entry_heap()
	{
	}

	slist_entry_heap::~slist_entry_heap()
	{
		destroy();
	}

	bool	slist_entry_heap::create()
	{
		if( lock_.create() == false )
		{
			return false;
		}

		if( heap_.create( HEAP_NO_SERIALIZE ) == false )
		{
			destroy();
			return false;
		}

		if( delayed_free_list_.create( common::slist::max ) == false )
		{
			destroy();
			return false;
		}

		return true;
	}

	void	slist_entry_heap::destroy()
	{
		if( lock_.lock() )
		{
			do_delayed_free_list();
			lock_.unlock();
		}

		delayed_free_list_.destroy();

		if( lock_.lock() )
		{
			heap_.destroy();
			lock_.unlock();
		}

		lock_.destroy();
	}

	SLIST_ENTRY*	slist_entry_heap::alloc( size_t size )
	{
		size += sizeof( SLIST_ENTRY );

		SLIST_ENTRY* p = nullptr;
		if( lock_.lock() )
		{
			do_delayed_free_list();
			p = reinterpret_cast< SLIST_ENTRY* >( heap_.alloc( size, MEMORY_ALLOCATION_ALIGNMENT ) );

			lock_.unlock();
		}

		return p;
	}

	SLIST_ENTRY*	slist_entry_heap::try_alloc( size_t size )
	{
		size += sizeof( SLIST_ENTRY );

		SLIST_ENTRY* p = nullptr;
		if( lock_.try_lock() )
		{
			do_delayed_free_list();
			p = reinterpret_cast< SLIST_ENTRY* >( heap_.alloc( size, MEMORY_ALLOCATION_ALIGNMENT ) );

			lock_.unlock();
		}

		return p;
	}

	bool	slist_entry_heap::free( SLIST_ENTRY* p )
	{
		if( p == nullptr )
		{
			return true;
		}

		bool result = false;
		if( lock_.try_lock() )
		{
			do_delayed_free_list();
			result = heap_.free( p );

			lock_.unlock();
		}
		else
		{
			result = delayed_free_list_.push( p );
			if( result == false )
			{
				if( lock_.lock() )
				{
					do_delayed_free_list();
					result = heap_.free( p );

					lock_.unlock();
				}
			}
		}

		return result;
	}

	size_t	slist_entry_heap::compact()
	{
		size_t free_size = 0;
		if( lock_.lock() )
		{
			do_delayed_free_list();
			free_size = heap_.compact();

			lock_.unlock();
		}

		return free_size;
	}

	void	slist_entry_heap::do_delayed_free_list()
	{
		SLIST_ENTRY* entry = delayed_free_list_.pop_all();
		while( entry != nullptr )
		{
			SLIST_ENTRY* p = entry;
			entry = entry->Next;
#ifdef	_DEBUG
			bool result =
#endif
			heap_.free( p );
#ifdef	_DEBUG
			assert( result == true );
#endif
		}
	}
}
